﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;

namespace OpenCalais
{
    public class OpenCalaisConnect
    {
        private Hashtable _cattypes = new Hashtable();

        private string _apikey;
        private bool _allowDistribution = false;
        private bool _allowSearch = false;
        private string _externalID = "";
        private string _submitter = "Open Calais Tags dotNet";
        private string _contentType = "text/html";
        private string _outputFormat = "xml/rdf";
        private bool _prettyTypes = true;
        private ArrayList _tags;


        public OpenCalaisConnect(string apiKey)
        {
            if (apiKey == "" || apiKey == null)
                throw new Exception("You must provide an OpenCalais API key");
            else
                _apikey = apiKey;
            PopulateCategoryTypes();
            _tags = new ArrayList();
        }

        #region HelperFunctions

        private void PopulateCategoryTypes()
        {
            _cattypes.Add("Anniversary", "Anniversary");
            _cattypes.Add("City", "City");
            _cattypes.Add("Company", "Company");
            _cattypes.Add("Continent", "Continent");
            _cattypes.Add("Country", "Country");
            _cattypes.Add("Currency", "Currency");
            _cattypes.Add("Date", "Date");
            _cattypes.Add("EmailAddress", "Email Address");
            _cattypes.Add("EntertainmentAwardEvent", "Entertainment Award Event");
            _cattypes.Add("Facility", "Facility");
            _cattypes.Add("FaxNumber", "Fax Number");
            _cattypes.Add("Holiday", "Holiday");
            _cattypes.Add("IndustryTerm", "Industry Term");
            _cattypes.Add("MarketIndex", "Market Index");
            _cattypes.Add("MedicalCondition", "Medical Condition");
            _cattypes.Add("MedicalTreatment", "Medical Treatment");
            _cattypes.Add("Movie", "Movie");
            _cattypes.Add("MusicAlbum", "Music Album");
            _cattypes.Add("MusicGroup", "Music Group");
            _cattypes.Add("NaturalDisaster", "Natural Disaster");
            _cattypes.Add("NaturalFeature", "Natural Feature");
            _cattypes.Add("OperatingSystem", "Operating System");
            _cattypes.Add("Organization", "Organization");
            _cattypes.Add("Person", "Person");
            _cattypes.Add("PhoneNumber", "Phone Number");
            _cattypes.Add("Position", "Position");
            _cattypes.Add("Product", "Product");
            _cattypes.Add("ProgrammingLanguage", "Programming Language");
            _cattypes.Add("ProvinceOrState", "Province or State");
            _cattypes.Add("PublishedMedium", "Published Medium");
            _cattypes.Add("RadioProgram", "Radio Program");
            _cattypes.Add("RadioStation", "Radio Station");
            _cattypes.Add("Region", "Region");
            _cattypes.Add("SportsEvent", "Sports Event");
            _cattypes.Add("SportsGame", "Sports Game");
            _cattypes.Add("SportsLeague", "Sports League");
            _cattypes.Add("Technology", "Technology");
            _cattypes.Add("Time", "Time");
            _cattypes.Add("TVShow", "TV Show");
            _cattypes.Add("TVStation", "TV Station");
            _cattypes.Add("URL", "URL");
        }

        private string NormaliseString(string value)
        {
            string[] postSplit = value.Split('/');
            /*string category = ;*/
            /*return cattypes[category].ToString();*/
            return _cattypes[postSplit[(postSplit.Length - 1)]].ToString();
        }

        private string ArrayListToString(ArrayList t)
        {
           string[] arry = (string[])_tags.ToArray(typeof(string));
           return string.Join(",", arry);
        }

        private bool InIgnoreList(string val)
        {
            bool inList = false;
            if (val == "URL")
                inList = true;
            else if (val.Length >= 7 && val.Substring(0, 7) == "http://")
                inList = true;
            return inList;
        }

        #endregion HelperFunctions

        #region Properties

        public bool AllowDistribution
        {
            get { return _allowDistribution; }
            set { _allowDistribution = value; }
        }

        public bool AllowSearch
        {
            get { return _allowSearch; }
            set { _allowSearch = value; }
        }

        public string ExternalID
        {
            get { return _externalID; }
            set { _externalID = value; }
        }

        public string Submitter
        {
            get { return _submitter; }
            set { _submitter = value; }
        }

        public string ContentType
        {
            get { return _contentType; }
            set { _contentType = value; }
        }

        public string OutputFormat
        {
            get { return _outputFormat; }
            set { _outputFormat = value; }
        }

        public bool PrettyTypes
        {
            get { return _prettyTypes; }
            set { _prettyTypes = value; }
        }

        public ArrayList Tags
        {
            //get { return ArrayListToString(_tags); }
            get { return _tags; }
        }

        #endregion Properties

        #region Private

        /// <summary>
        /// Builds XML parameters for API request
        /// </summary>
        private string BuildParam()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<c:params xmlns:c=\"http://s.opencalais.com/1/pred/\"");
            sb.Append(" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">");
            sb.Append("	<c:processingDirectives c:contentType=\"");
            sb.Append(_contentType);
            sb.Append("\" ");
            sb.Append("c:enableMetadataType=\"GenericRelations,SocialTags\" c:outputFormat=\"");
            sb.Append(_outputFormat);
            sb.Append("\"></c:processingDirectives>");
            sb.Append("	<c:userDirectives c:allowDistribution=\"");
            sb.Append(_allowDistribution);
            sb.Append("\" c:allowSearch=\"");
            sb.Append(_allowSearch);
            sb.Append("\" c:externalID=\"");
            sb.Append(_externalID);
            sb.Append("\" c:submitter=\"");
            sb.Append(_submitter);
            sb.Append("\"></c:userDirectives>");
            sb.Append("	<c:externalMetadata></c:externalMetadata>");
            sb.Append("</c:params>");

            return sb.ToString();
        }

        #endregion Private

        #region Public
        public void GetTagsFromXML(string xmlData, string xpath, bool cat)
        {
            /*  //c:name gets "name" values
            * //c:name/preceding-sibling::*[1]/@rdf:resource gets categories?
            * 
            */
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xmlData);
            XmlNamespaceManager nameManager = new XmlNamespaceManager(new NameTable());
            nameManager.AddNamespace("c", "http://s.opencalais.com/1/pred/");
            nameManager.AddNamespace("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
            XPathNavigator nav = xmlDoc.CreateNavigator();
            XPathExpression exp = nav.Compile(xpath);
            exp.SetContext(nameManager);

            XPathNodeIterator nodes = xmlDoc.CreateNavigator().Select(exp);

            foreach (XPathNavigator node in nodes)
            {
                string value = node.Value.ToString();
                if (cat)
                    value = NormaliseString(value);
                
                //Remove duplicates
                if (!_tags.Contains(value) && !InIgnoreList(value))
                    _tags.Add(value);
            }
        }
        /// <summary>
        /// Request Tags for supplied content via SOAP
        /// </summary>
        /// <param name="content">Text to generate tags from</param>
        public void GetContent(string content)
        {
            string response = "No results";

            com.opencalais.api.calais call = new global::OpenCalais.com.opencalais.api.calais();

            try
            {
                response = call.Enlighten(_apikey, content, BuildParam());
                // Get Categories
                GetTagsFromXML(response,"//c:name/preceding-sibling::*[1]/@rdf:resource",true);
                // Get Individual Items
                GetTagsFromXML(response,"//c:name",false);
            }
            catch (Exception ex)
            {
                throw new SystemException("Error: " + ex.Message);
            }            
        }

        #endregion Public       
    }
}
